//
// @Auther: by wujehy 
// @Email:wujehy@qq.com
// @Data:20-5-12
// @Time:上午11:34
//

#include <group_session_builder.h>
#include <group_cipher.h>
#include <protocol.h>
#include <signal_protocol_internal.h>
#include <cstring>
#include "GroupSession.h"

using namespace GeeJoan::E2EE::Common;
const std::string &GroupSession::getTeamname() const
{
    return teamname;
}

void GroupSession::setTeamname(const std::string &teamname)
{
    GroupSession::teamname = teamname;
}

void test_log(int level, const char *message, size_t len, void *user_data)
{
    switch (level)
    {
        case SG_LOG_ERROR:
            fprintf(stderr, "[ERROR] %s\n", message);
            break;
        case SG_LOG_WARNING:
            fprintf(stderr, "[WARNING] %s\n", message);
            break;
        case SG_LOG_NOTICE:
            fprintf(stderr, "[NOTICE] %s\n", message);
            break;
        case SG_LOG_INFO:
            fprintf(stderr, "[INFO] %s\n", message);
            break;
        case SG_LOG_DEBUG:
            fprintf(stderr, "[DEBUG] %s\n", message);
            break;
        default:
            fprintf(stderr, "[%d] %s\n", level, message);
            break;
    }
}

//signal_context *global_context;
//pthread_mutex_t global_mutex;
//pthread_mutexattr_t global_mutex_attr;



GroupSession::GroupSession()
{

    std::cout << "init team " << std::endl;

    int result = common_signal_context_create(&m_global_context, 0);
    signal_context_set_log_function(m_global_context, test_log);

    common_setup_crypto_provider(m_global_context, nullptr);
}

GroupSession::~GroupSession()
{
    std::cout << "delete team " << teamname << std::endl;

    if (my_store)signal_protocol_store_context_destroy(my_store);

    if (my_sender_key_name) delete my_sender_key_name;

    if (my_session_builder)group_session_builder_free(my_session_builder);
}

void GroupSession::createDistribution(std::string &outDistributionPackage)
{

    int result = group_session_builder_create(&my_session_builder, my_store, m_global_context);

    result = group_cipher_create(&my_cipher, my_store, my_sender_key_name, m_global_context);
    sender_key_distribution_message *sent_alice_distribution_message = 0;
    if (my_sender_key_name)
        result = group_session_builder_create_session(my_session_builder, &sent_alice_distribution_message,
                                                      my_sender_key_name);

    signal_buffer *serialized_distribution_message = ciphertext_message_get_serialized(
            (ciphertext_message *) sent_alice_distribution_message);

    outDistributionPackage = std::string((char *) serialized_distribution_message->data,
                                         serialized_distribution_message->len);

    SIGNAL_UNREF(sent_alice_distribution_message);
}


void GroupSession::updataDistribution(std::string sendername, int deviceid, std::string distribution_message)
{
//    解出cipher

    sender_key_distribution_message *received_alice_distribution_message = 0;

    int result = sender_key_distribution_message_deserialize(&received_alice_distribution_message,
                                                             (uint8_t *) distribution_message.c_str(),
                                                             distribution_message.length(), m_global_context);

    /* Processing Alice's distribution message */



    signal_protocol_sender_key_name *current_member_session = new signal_protocol_sender_key_name;
    current_member_session->group_id_len = this->teamname.length();
    current_member_session->group_id = this->teamname.c_str();
    // TODO 内存释放问题 有类控制
    char *currentusernen = new char[sendername.length()];
    memcpy(currentusernen, sendername.c_str(), sendername.length());
    current_member_session->sender.name = currentusernen;
    current_member_session->sender.name_len = sendername.length();
    current_member_session->sender.device_id = deviceid;
    verterChar.push_back(currentusernen);
    group_cipher *bob_group_cipher = 0;
    result = group_cipher_create(&bob_group_cipher, my_store, current_member_session, m_global_context);

    std::string sesionid = sendername + "." + std::to_string(deviceid);

    auto isok = memberCipher.insert(std::pair<std::string, group_cipher *>(sesionid, bob_group_cipher));
    if (isok.second)
    {
        std::cout << "Yes memberCipher.insert is ok " << std::endl;
    }
    result = group_session_builder_process_session(my_session_builder, current_member_session,
                                                   received_alice_distribution_message);



    //this->memberCipher.insert()
}

void GroupSession::removeTeamMember(std::string teamname)
{

}

void GroupSession::encryptMessage(std::string inMessage, std::map<std::string, MessagePackageDagaType> &outMessage)
{
    ciphertext_message *ciphertext_from_alice = 0;
//    result = group_cipher_encrypt(alice_group_cipher,
//                                  (const uint8_t *)alice_plaintext, alice_plaintext_len,
//                                  &ciphertext_from_alice);
    for (auto user : memberCipher)
    {
        if(ciphertext_from_alice){
//            SIGNAL_UNREF(ciphertext_from_alice);
        }
        // 加密消息
        group_cipher_encrypt(my_cipher , (uint8_t*)inMessage.c_str() , inMessage.length() , &ciphertext_from_alice);

        signal_buffer *serialized = ciphertext_message_get_serialized((ciphertext_message *)ciphertext_from_alice);

        MessagePackageDagaType temp{
            .type = ciphertext_message_get_type((ciphertext_message *)ciphertext_from_alice),
            .serialze = std::string((char*)serialized->data , serialized->len)
        };

        outMessage.insert(std::pair<std::string ,MessagePackageDagaType >(user.first , temp));
        signal_buffer_free(serialized);
    }
}

void GroupSession::decryptMessage(std::string username, int deviceid, MessagePackageDagaType &inMessage,
                                  std::string &outMessage)
{
    std::string sessionid = username + "." + std::to_string(deviceid);
    auto iter = memberCipher.find(sessionid);
    if (iter != memberCipher.end())
    {
        // 存在 , 解包

        signal_buffer *plaintext_from_alice = 0;


        sender_key_message *result_message = 0;

        int result = sender_key_message_deserialize(&result_message,
                                                    (uint8_t *) inMessage.serialze.c_str(),
                                                    inMessage.serialze.length(),
                                                    m_global_context);
        result = group_cipher_decrypt(iter->second, result_message, 0, &plaintext_from_alice);

        outMessage = std::string((char *) plaintext_from_alice->data, plaintext_from_alice->len);


    } else
    {
        // TODO
        // 不存在 创建
//        signal_protocol_sender_key_name *current_member_session = new signal_protocol_sender_key_name;
//        current_member_session->group_id_len = this->teamname.length();
//        current_member_session->group_id = this->teamname.c_str();
//        char *currentusernen = new char[username.length()];
//        memcpy(currentusernen ,username.c_str() ,username.length()  );
//        current_member_session->sender.name = currentusernen;
//        current_member_session->sender.name_len = username.length();
//        current_member_session->sender.device_id = deviceid;
//        verterChar.push_back(currentusernen);
//        group_cipher *bob_group_cipher = 0;
//        int result = group_cipher_create(&bob_group_cipher, my_store, current_member_session,  global_context);
//        ciphertext_message *ciphertext_from_alice = 0;
////        result = group_cipher_encrypt(alice_group_cipher,
////                                      (const uint8_t *)alice_plaintext, alice_plaintext_len,
////                                      &ciphertext_from_alice);
////        result = sender_key_distribution_message_deserialize(&received_alice_distribution_message,
////                                                             signal_buffer_data(serialized_distribution_message),
////                                                             signal_buffer_len(serialized_distribution_message),
////                                                             global_context);
//
//        result = group_cipher_decrypt(bob_group_cipher, (sender_key_message *)ciphertext_from_alice, 0, &plaintext_from_alice);

    }
}

const std::string &GroupSession::getMyName() const
{
    return myName;
}

void GroupSession::setMyName(const std::string &myName)
{
    GroupSession::myName = myName;
}

int GroupSession::getMyDeviceid() const
{
    return myDeviceid;
}

void GroupSession::setMyDeviceid(int myDeviceid)
{
    GroupSession::myDeviceid = myDeviceid;
}

void GroupSession::initLocal(std::string myName, int myDeviceid, std::string teamName)
{
    setTeamname(teamName);
    setMyName(myName);
    setMyDeviceid(myDeviceid);

    my_sender_key_name = new signal_protocol_sender_key_name;

    my_sender_key_name->group_id_len = this->teamname.length();
    my_sender_key_name->group_id = this->teamname.c_str();
    my_sender_key_name->sender.name = this->myName.c_str();
    my_sender_key_name->sender.name_len = this->myName.length();
    my_sender_key_name->sender.device_id = this->myDeviceid;

    setup_common_store_context(&my_store, m_global_context);

    //signal_context *global_context , ec_public_key *identity_key_public ,  ec_private_key *identity_key_private , uint32_t local_registration_id
    setup_common_identity_key_store(my_store, m_global_context ,ik, local_id);
    setup_common_sender_key_store(my_store, m_global_context);
}

void GroupSession::setIk(KeyPairDataType &keyPairData)
{
    deserializationEcKeyPair(keyPairData , &ik , m_global_context);
}

void GroupSession::setLocalId(int localId)
{
    local_id = localId;
}

signal_context *GroupSession::getMGlobalContext() const
{
    return m_global_context;
}


MemberCipherData::~MemberCipherData()
{
    if (session)
    {
        delete session;
    }

    if (cipher)
    {
        group_cipher_free(cipher);
    }
}

void GroupSession::setLock(lockFunction test_lock,lockFunction test_unlock)
{

    signal_context_set_locking_functions(m_global_context, test_lock, test_unlock);

}
